package com.tian.code.tool.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.tian.code.tool.dao.TableDao;

import freemarker.template.Configuration;
import freemarker.template.Template;

@Service
public class CodeService {

	protected Log logger = LogFactory.getLog(getClass());

	private final String JAVA = ".java";
	private final String XML = ".xml";

	@Autowired
	private TableDao tableDao;

	@Value("${tabFile}")
	private String tabFile;
	// @Value("${templatePath}")
	private String templatePath;
	@Value("${model.output}")
	private String modelOutput;
	@Value("${dao.output}")
	private String daoOutput;

	private Configuration cfg = new Configuration();
	private Template modelTemplate;
	private Template xmlTemplate;
	private Template daoTemplate;
	private Template configTemplate;
	private Template modelKeyTemplate;

	public CodeService() {
		templatePath = Thread.currentThread().getContextClassLoader().getResource("./").getPath() + "/template/";
	}

	@Value("${model.package.name}")
	private String modelPackageName;
	@Value("${model.temlate.name}")
	private String modelTemplateName;

	@Value("${xml.temlate.name}")
	private String xmlTemplateName;

	@Value("${dao.temlate.name}")
	private String daoTemplateName;
	@Value("${dao.package.name}")
	private String daoPackageName;

	@Value("${config.temlate.name}")
	private String configTemplateName;

	@Value("${modelKey.temlate.name}")
	private String modelKeyTemplateName;

	public void buildModelCode() throws Exception {
		cfg.setDirectoryForTemplateLoading(new File(templatePath));
		modelTemplate = cfg.getTemplate(modelTemplateName);
		xmlTemplate = cfg.getTemplate(xmlTemplateName);
		daoTemplate = cfg.getTemplate(daoTemplateName);
		configTemplate = cfg.getTemplate(configTemplateName);
		modelKeyTemplate = cfg.getTemplate(modelKeyTemplateName);

		List<String> tabs = getBuildTabs();
		HashMap<String, String> tabComments = tableDao.getTableComments();
		List<String> modelXmls = new ArrayList<String>();

		for (String tabName : tabs) {
			if (!tableDao.checkTabIsExist(tabName)) {
				logger.warn("！！！！表不存在！！！！，table_name：" + tabName);
				continue;
			}

			String modelOutPathName = modelOutput + getJavaNameUpper(tabName) + JAVA;
			String modelPKOutPathName = modelOutput + getJavaNameUpper(tabName) + "PK" + JAVA;
			String xmlOutPathName = modelOutput + getJavaNameUpper(tabName) + XML;
			String daoOutPathName = daoOutput + getJavaNameUpper(tabName) + "Dao" + JAVA;
			modelXmls.add(modelPackageName.replaceAll("\\.", "/") + "/" + getJavaNameUpper(tabName) + XML);

			ModelBase base = new ModelBase();
			base.setClassName(getJavaNameUpper(tabName));
			base.setModelPackageName(modelPackageName);
			base.setDaoPackageName(daoPackageName);
			base.setTableComment(tabComments.get(tabName));
			base.setTableName(tabName);

			//
			List<String> importNames = new ArrayList<String>();
			
			// 表所有字段注释
			HashMap<String, String> colComments = tableDao.getColComments(tabName);
			// 表所有字段信息
			List<Map<String, Object>> tabColumns = tableDao.geTabColumns(tabName);
			// 表字段转JAVA文件
			List<ModelBody> bodys = convertColToBody(tabColumns, colComments, importNames);

			Map<String, Object> root = new HashMap<String, Object>();
			root.put("base", base);
			root.put("bodys", bodys);
			root.put("importNames", importNames);

			// 生成model
			Writer out = new OutputStreamWriter(new FileOutputStream(modelOutPathName), "UTF-8");
			modelTemplate.process(root, out);
			out.flush();
			out.close();
			logger.debug("完成model.java ->> table:" + tabName);

			// 表主键
			List<String> pks = tableDao.getTableColKey(tabName);
			// 主键字段信息

			// 需要表主键转JAVA文件

			// 主键
			List<Map<String, Object>> pkColumns = new ArrayList<Map<String, Object>>();
			// 非主键
			List<Map<String, Object>> notPkColumns = new ArrayList<Map<String, Object>>();

			for (String s : pks) {
				for (Map<String, Object> map : tabColumns) {
					if (s.equals(map.get("COLUMN_NAME").toString()))
						pkColumns.add(map);
					else
						notPkColumns.add(map);
				}
			}
			importNames = new ArrayList<String>();
			List<ModelBody> pkBodys = convertColToBody(pkColumns, colComments, importNames);
			List<ModelBody> notPkBodys = convertColToBody(notPkColumns, colComments, importNames);

			if (pks.size() > 1) {
				base.setPkParaClassName(modelPackageName + "." + base.getClassName() + "PK");
				Map<String, Object> keyRoot = new HashMap<String, Object>();
				keyRoot.put("base", base);
				keyRoot.put("bodys", pkBodys);
				keyRoot.put("importNames", importNames);
				out = new OutputStreamWriter(new FileOutputStream(modelPKOutPathName), "UTF-8");
				modelKeyTemplate.process(root, out);
				out.flush();
				out.close();
				logger.debug("完成PK.java ->> table:" + tabName);
			} else if (pks.size() == 1) {
				base.setPkParaClassName(pkBodys.get(0).getJavaType());
			} else {
				base.setPkParaClassName("empty");
			}

			// 生成xml
			root.put("pkBodys", pkBodys);
			root.put("notPkBodys", notPkBodys);
			out = new OutputStreamWriter(new FileOutputStream(xmlOutPathName), "UTF-8");
			xmlTemplate.process(root, out);
			out.flush();
			out.close();
			logger.debug("完成model.xml ->> table:" + tabName);

			// 生成dao
			out = new OutputStreamWriter(new FileOutputStream(daoOutPathName), "UTF-8");
			daoTemplate.process(root, out);
			out.flush();
			out.close();
			logger.debug("完成dao.java ->> table:" + tabName);

		}

		// 生成config
		Writer out = new OutputStreamWriter(new FileOutputStream(modelOutput + "Config.xml"), "UTF-8");
		Map<String, Object> root = new HashMap<String, Object>();
		root.put("modelXmls", modelXmls);
		configTemplate.process(root, out);
		out.flush();
		out.close();
		logger.debug("完成Config.xml");

	}

	private List<ModelBody> convertColToBody(List<Map<String, Object>> tabColumns, HashMap<String, String> colComments,
			List<String> importNames) throws Exception {
		List<ModelBody> bodys = new ArrayList<ModelBody>();
		ModelBody body = null;

		for (Map<String, Object> map : tabColumns) {
			body = new ModelBody();
			body.setColComment(colComments.get(map.get("COLUMN_NAME")));
			body.setColName(map.get("COLUMN_NAME").toString());

			String tmpType = getJavaType(map.get("DATA_TYPE").toString(),
					Integer.valueOf(map.get("DATA_LENGTH").toString()));
			body.setJavaType(tmpType);

			if (tmpType.equals("Date") && importNames.size() < 1)
				importNames.add("java.util.Date");

			body.setMethodName(getJavaNameUpper(map.get("COLUMN_NAME").toString()));
			body.setPropertyName(getJavaNameLower(map.get("COLUMN_NAME").toString()));
			body.setColType(map.get("DATA_TYPE").toString());
			bodys.add(body);
		}

		return bodys;

	}

	/**
	 * 将数据库类型转换成对应的JAVA类型
	 * 
	 * @param jdbcType
	 * @return
	 * @throws Exception
	 */
	private String getJavaType(String jdbcType, Integer length) throws Exception {
		jdbcType = jdbcType.toLowerCase();
		if ("char".equals(jdbcType) || "varchar".equals(jdbcType) || "nvarchar".equals(jdbcType)
				|| "varchar2".equals(jdbcType)) {
			return "String";
		} else if ("int".equals(jdbcType)) {
			return "Integer";
		} else if ("number".equals(jdbcType)) {
			if (length < 10)
				return "Integer";
			else
				return "Long";
		} else if ("timestamp".equals(jdbcType) || "date".equals(jdbcType) || "datetime".equals(jdbcType)) {
			return "Date";
		} else if ("decimal".equals(jdbcType)) {
			return "Double";
		} else if ("image".equals(jdbcType)) {
			return "byte[]";
		} else if ("smallint".equals(jdbcType)) {
			return "int";
		} else {
			throw new Exception("无法转换类型" + jdbcType);
		}
	}

	/**
	 * 首字母小写,jdbc_Type->jdbcType
	 * 
	 * @param jdbcName
	 * @return
	 */
	private String getJavaNameLower(String jdbcName) {
		String regex = "_";
		String result = jdbcName.toLowerCase();

		if (result.indexOf(regex) != -1) {
			String ary[] = result.split(regex);
			result = "";
			for (int i = 0; i < ary.length; i++) {
				if (i != 0) {
					result += ary[i].substring(0, 1).toUpperCase() + ary[i].substring(1, ary[i].length());
				} else {
					result = ary[0];
				}
			}
		}
		return result;
	}

	/**
	 * 首字母大写,jdbc_Type->JdbcType
	 * 
	 * @param jdbcName
	 * @return
	 */
	private String getJavaNameUpper(String jdbcName) {
		String regex = "_";
		String result = jdbcName.toLowerCase();

		if (result.indexOf(regex) != -1) {
			String ary[] = result.split(regex);
			result = "";
			for (int i = 0; i < ary.length; i++) {
				result += ary[i].substring(0, 1).toUpperCase() + ary[i].substring(1, ary[i].length());
			}
		} else {
			result = result.substring(0, 1).toUpperCase() + result.substring(1, result.length());
		}
		return result;
	}

	/**
	 * 
	 * @return
	 * @throws IOException
	 */
	private List<String> getBuildTabs() throws IOException {
		List<String> list = new ArrayList<String>();
		BufferedReader br = new BufferedReader(new FileReader(new File(tabFile)));
		String line = null;
		while ((line = br.readLine()) != null) {
			if (!line.startsWith("#")) {
				list.add(line);
			}
		}
		return list;
	}

	public void buildXmlCode() {

	}

	public void buildDaoCode() {

	}

}
